home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2007 December / PCWKCD1207B.iso / Blogowanie poza sfera / Flock 1.0 beta / flock-1.0RC3.en-US.win32.exe / flock / components / nsUrlClassifierListManager.js < prev    next >
Text File  |  2007-10-18  |  33KB  |  933 lines

  1. /* ***** BEGIN LICENSE BLOCK *****
  2.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3.  *
  4.  * The contents of this file are subject to the Mozilla Public License Version
  5.  * 1.1 (the "License"); you may not use this file except in compliance with
  6.  * the License. You may obtain a copy of the License at
  7.  * http://www.mozilla.org/MPL/
  8.  *
  9.  * Software distributed under the License is distributed on an "AS IS" basis,
  10.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  11.  * for the specific language governing rights and limitations under the
  12.  * License.
  13.  *
  14.  * The Original Code is Url Classifier code
  15.  *
  16.  * The Initial Developer of the Original Code is
  17.  * Google Inc.
  18.  * Portions created by the Initial Developer are Copyright (C) 2006
  19.  * the Initial Developer. All Rights Reserved.
  20.  *
  21.  * Contributor(s):
  22.  *   Tony Chang <tony@ponderer.org>
  23.  *
  24.  * Alternatively, the contents of this file may be used under the terms of
  25.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  26.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  27.  * in which case the provisions of the GPL or the LGPL are applicable instead
  28.  * of those above. If you wish to allow use of your version of this file only
  29.  * under the terms of either the GPL or the LGPL, and not to allow others to
  30.  * use your version of this file under the terms of the MPL, indicate your
  31.  * decision by deleting the provisions above and replace them with the notice
  32.  * and other provisions required by the GPL or the LGPL. If you do not delete
  33.  * the provisions above, a recipient may use your version of this file under
  34.  * the terms of any one of the MPL, the GPL or the LGPL.
  35.  *
  36.  * ***** END LICENSE BLOCK ***** */
  37.  
  38. const Cc = Components.classes;
  39. const Ci = Components.interfaces;
  40.  
  41. /* ***** BEGIN LICENSE BLOCK *****
  42.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  43.  *
  44.  * The contents of this file are subject to the Mozilla Public License Version
  45.  * 1.1 (the "License"); you may not use this file except in compliance with
  46.  * the License. You may obtain a copy of the License at
  47.  * http://www.mozilla.org/MPL/
  48.  *
  49.  * Software distributed under the License is distributed on an "AS IS" basis,
  50.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  51.  * for the specific language governing rights and limitations under the
  52.  * License.
  53.  *
  54.  * The Original Code is Google Safe Browsing.
  55.  *
  56.  * The Initial Developer of the Original Code is Google Inc.
  57.  * Portions created by the Initial Developer are Copyright (C) 2006
  58.  * the Initial Developer. All Rights Reserved.
  59.  *
  60.  * Contributor(s):
  61.  *   Niels Provos <niels@google.com> (original author)
  62.  *   Fritz Schneider <fritz@google.com>
  63.  *
  64.  * Alternatively, the contents of this file may be used under the terms of
  65.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  66.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  67.  * in which case the provisions of the GPL or the LGPL are applicable instead
  68.  * of those above. If you wish to allow use of your version of this file only
  69.  * under the terms of either the GPL or the LGPL, and not to allow others to
  70.  * use your version of this file under the terms of the MPL, indicate your
  71.  * decision by deleting the provisions above and replace them with the notice
  72.  * and other provisions required by the GPL or the LGPL. If you do not delete
  73.  * the provisions above, a recipient may use your version of this file under
  74.  * the terms of any one of the MPL, the GPL or the LGPL.
  75.  *
  76.  * ***** END LICENSE BLOCK ***** */
  77.  
  78.  
  79. // A class that manages lists, namely white and black lists for
  80. // phishing or malware protection. The ListManager knows how to fetch,
  81. // update, and store lists, and knows the "kind" of list each is (is
  82. // it a whitelist? a blacklist? etc). However it doesn't know how the
  83. // lists are serialized or deserialized (the wireformat classes know
  84. // this) nor the specific format of each list. For example, the list
  85. // could be a map of domains to "1" if the domain is phishy. Or it
  86. // could be a map of hosts to regular expressions to match, who knows?
  87. // Answer: the trtable knows. List are serialized/deserialized by the
  88. // wireformat reader from/to trtables, and queried by the listmanager.
  89. //
  90. // There is a single listmanager for the whole application.
  91. //
  92. // The listmanager is used only in privacy mode; in advanced protection
  93. // mode a remote server is queried.
  94. //
  95. // How to add a new table:
  96. // 1) get it up on the server
  97. // 2) add it to tablesKnown
  98. // 3) if it is not a known table type (trtable.js), add an implementation
  99. //    for it in trtable.js
  100. // 4) add a check for it in the phishwarden's isXY() method, for example
  101. //    isBlackURL()
  102. //
  103. // TODO: obviously the way this works could use a lot of improvement. In
  104. //       particular adding a list should just be a matter of adding
  105. //       its name to the listmanager and an implementation to trtable
  106. //       (or not if a talbe of that type exists). The format and semantics
  107. //       of the list comprise its name, so the listmanager should easily
  108. //       be able to figure out what to do with what list (i.e., no
  109. //       need for step 4).
  110. // TODO more comprehensive update tests, for example add unittest check 
  111. //      that the listmanagers tables are properly written on updates
  112.  
  113. /**
  114.  * The base pref name for where we keep table version numbers.
  115.  * We add append the table name to this and set the value to
  116.  * the version.  E.g., tableversion.goog-black-enchash may have
  117.  * a value of 1.1234.
  118.  */
  119. const kTableVersionPrefPrefix = "urlclassifier.tableversion.";
  120.  
  121. // How frequently we check for updates (30 minutes)
  122. const kUpdateInterval = 30 * 60 * 1000;
  123.  
  124. /**
  125.  * A ListManager keeps track of black and white lists and knows
  126.  * how to update them.
  127.  *
  128.  * @constructor
  129.  */
  130. function PROT_ListManager() {
  131.   this.debugZone = "listmanager";
  132.   G_debugService.enableZone(this.debugZone);
  133.  
  134.   this.currentUpdateChecker_ = null;   // set when we toggle updates
  135.   this.prefs_ = new G_Preferences();
  136.  
  137.   this.updateserverURL_ = null;
  138.  
  139.   // The lists we know about and the parses we can use to read
  140.   // them. Default all to the earlies possible version (1.-1); this
  141.   // version will get updated when successfully read from disk or
  142.   // fetch updates.
  143.   this.tablesKnown_ = {};
  144.   this.isTesting_ = false;
  145.   
  146.   if (this.isTesting_) {
  147.     // populate with some tables for unittesting
  148.     this.tablesKnown_ = {
  149.       // A major version of zero means local, so don't ask for updates       
  150.       "test1-foo-domain" : new PROT_VersionParser("test1-foo-domain", 0, -1),
  151.       "test2-foo-domain" : new PROT_VersionParser("test2-foo-domain", 0, -1),
  152.       "test-white-domain" : 
  153.         new PROT_VersionParser("test-white-domain", 0, -1, true /* require mac*/),
  154.       "test-mac-domain" :
  155.         new PROT_VersionParser("test-mac-domain", 0, -1, true /* require mac */)
  156.     };
  157.     
  158.     // expose the object for unittesting
  159.     this.wrappedJSObject = this;
  160.   }
  161.  
  162.   this.tablesData = {};
  163.  
  164.   this.observerServiceObserver_ = new G_ObserverServiceObserver(
  165.                                           'xpcom-shutdown',
  166.                                           BindToObject(this.shutdown_, this),
  167.                                           true /*only once*/);
  168.  
  169.   // Lazily create urlCrypto (see tr-fetcher.js)
  170.   this.urlCrypto_ = null;
  171.   
  172.   this.requestBackoff_ = new RequestBackoff(3 /* num errors */,
  173.                                    10*60*1000 /* error time, 10min */,
  174.                                    60*60*1000 /* backoff interval, 60min */,
  175.                                    6*60*60*1000 /* max backoff, 6hr */);
  176. }
  177.  
  178. /**
  179.  * xpcom-shutdown callback
  180.  * Delete all of our data tables which seem to leak otherwise.
  181.  */
  182. PROT_ListManager.prototype.shutdown_ = function() {
  183.   for (var name in this.tablesData) {
  184.     delete this.tablesData[name];
  185.   }
  186. }
  187.  
  188. /**
  189.  * Set the url we check for updates.  If the new url is valid and different,
  190.  * update our table list.
  191.  * 
  192.  * After setting the update url, the caller is responsible for registering
  193.  * tables and then toggling update checking.  All the code for this logic is
  194.  * currently in browser/components/safebrowsing.  Maybe it should be part of
  195.  * the listmanger?
  196.  */
  197. PROT_ListManager.prototype.setUpdateUrl = function(url) {
  198.   G_Debug(this, "Set update url: " + url);
  199.   if (url != this.updateserverURL_) {
  200.     this.updateserverURL_ = url;
  201.     this.requestBackoff_.reset();
  202.     
  203.     // Remove old tables which probably aren't valid for the new provider.
  204.     for (var name in this.tablesData) {
  205.       delete this.tablesData[name];
  206.       delete this.tablesKnown_[name];
  207.     }
  208.   }
  209. }
  210.  
  211. /**
  212.  * Set the crypto key url.
  213.  * @param url String
  214.  */
  215. PROT_ListManager.prototype.setKeyUrl = function(url) {
  216.   G_Debug(this, "Set key url: " + url);
  217.   if (!this.urlCrypto_)
  218.     this.urlCrypto_ = new PROT_UrlCrypto();
  219.   
  220.   this.urlCrypto_.manager_.setKeyUrl(url);
  221. }
  222.  
  223. /**
  224.  * Register a new table table
  225.  * @param tableName - the name of the table
  226.  * @param opt_requireMac true if a mac is required on update, false otherwise
  227.  * @returns true if the table could be created; false otherwise
  228.  */
  229. PROT_ListManager.prototype.registerTable = function(tableName, 
  230.                                                     opt_requireMac) {
  231.   var table = new PROT_VersionParser(tableName, 1, -1, opt_requireMac);
  232.   if (!table)
  233.     return false;
  234.   this.tablesKnown_[tableName] = table;
  235.   this.tablesData[tableName] = newUrlClassifierTable(tableName);
  236.  
  237.   return true;
  238. }
  239.  
  240. /**
  241.  * Enable updates for some tables
  242.  * @param tables - an array of table names that need updating
  243.  */
  244. PROT_ListManager.prototype.enableUpdate = function(tableName) {
  245.   var changed = false;
  246.   var table = this.tablesKnown_[tableName];
  247.   if (table) {
  248.     G_Debug(this, "Enabling table updates for " + tableName);
  249.     table.needsUpdate = true;
  250.     changed = true;
  251.   }
  252.  
  253.   if (changed === true)
  254.     this.maybeToggleUpdateChecking();
  255. }
  256.  
  257. /**
  258.  * Disables updates for some tables
  259.  * @param tables - an array of table names that no longer need updating
  260.  */
  261. PROT_ListManager.prototype.disableUpdate = function(tableName) {
  262.   var changed = false;
  263.   var table = this.tablesKnown_[tableName];
  264.   if (table) {
  265.     G_Debug(this, "Disabling table updates for " + tableName);
  266.     table.needsUpdate = false;
  267.     changed = true;
  268.   }
  269.  
  270.   if (changed === true)
  271.     this.maybeToggleUpdateChecking();
  272. }
  273.  
  274. /**
  275.  * Determine if we have some tables that need updating.
  276.  */
  277. PROT_ListManager.prototype.requireTableUpdates = function() {
  278.   for (var type in this.tablesKnown_) {
  279.     // All tables with a major of 0 are internal tables that we never
  280.     // update remotely.
  281.     if (this.tablesKnown_[type].major == 0)
  282.       continue;
  283.      
  284.     // Tables that need updating even if other tables dont require it
  285.     if (this.tablesKnown_[type].needsUpdate)
  286.       return true;
  287.   }
  288.  
  289.   return false;
  290. }
  291.  
  292. /**
  293.  * Start managing the lists we know about. We don't do this automatically
  294.  * when the listmanager is instantiated because their profile directory
  295.  * (where we store the lists) might not be available.
  296.  */
  297. PROT_ListManager.prototype.maybeStartManagingUpdates = function() {
  298.   if (this.isTesting_)
  299.     return;
  300.  
  301.   // We might have been told about tables already, so see if we should be
  302.   // actually updating.
  303.   this.maybeToggleUpdateChecking();
  304. }
  305.  
  306. /**
  307.  * Determine if we have any tables that require updating.  Different
  308.  * Wardens may call us with new tables that need to be updated.
  309.  */ 
  310. PROT_ListManager.prototype.maybeToggleUpdateChecking = function() {
  311.   // If we are testing or dont have an application directory yet, we should
  312.   // not start reading tables from disk or schedule remote updates
  313.   if (this.isTesting_)
  314.     return;
  315.  
  316.   // We update tables if we have some tables that want updates.  If there
  317.   // are no tables that want to be updated - we dont need to check anything.
  318.   if (this.requireTableUpdates() === true) {
  319.     G_Debug(this, "Starting managing lists");
  320.     this.startUpdateChecker();
  321.  
  322.     // Multiple warden can ask us to reenable updates at the same time, but we
  323.     // really just need to schedule a single update.
  324.     if (!this.currentUpdateChecker_) {
  325.       // If the user has never downloaded tables, do the check now.
  326.       // If the user has tables, add a fuzz of a few minutes.
  327.       this.loadTableVersions_();
  328.       var hasTables = false;
  329.       for (var table in this.tablesKnown_) {
  330.         if (this.tablesKnown_[table].minor != -1) {
  331.           hasTables = true;
  332.           break;
  333.         }
  334.       }
  335.  
  336.       var initialUpdateDelay = 3000;
  337.       if (hasTables) {
  338.         // Add a fuzz of 0-5 minutes.
  339.         initialUpdateDelay += Math.floor(Math.random() * (5 * 60 * 1000));
  340.       }
  341.       this.currentUpdateChecker_ =
  342.         new G_Alarm(BindToObject(this.checkForUpdates, this),
  343.                     initialUpdateDelay);
  344.     }
  345.   } else {
  346.     G_Debug(this, "Stopping managing lists (if currently active)");
  347.     this.stopUpdateChecker();                    // Cancel pending updates
  348.   }
  349. }
  350.  
  351. /**
  352.  * Start periodic checks for updates. Idempotent.
  353.  * We want to distribute update checks evenly across the update period (an
  354.  * hour).  To do this, we pick a random number of time between 0 and 30
  355.  * minutes.  The client first checks at 15 + rand, then every 30 minutes after
  356.  * that.
  357.  */
  358. PROT_ListManager.prototype.startUpdateChecker = function() {
  359.   this.stopUpdateChecker();
  360.   
  361.   // Schedule the first check for between 15 and 45 minutes.
  362.   var repeatingUpdateDelay = kUpdateInterval / 2;
  363.   repeatingUpdateDelay += Math.floor(Math.random() * kUpdateInterval);
  364.   this.updateChecker_ = new G_Alarm(BindToObject(this.initialUpdateCheck_,
  365.                                                  this),
  366.                                     repeatingUpdateDelay);
  367. }
  368.  
  369. /**
  370.  * Callback for the first update check.
  371.  * We go ahead and check for table updates, then start a regular timer (once
  372.  * every 30 minutes).
  373.  */
  374. PROT_ListManager.prototype.initialUpdateCheck_ = function() {
  375.   this.checkForUpdates();
  376.   this.updateChecker_ = new G_Alarm(BindToObject(this.checkForUpdates, this), 
  377.                                     kUpdateInterval, true /* repeat */);
  378. }
  379.  
  380. /**
  381.  * Stop checking for updates. Idempotent.
  382.  */
  383. PROT_ListManager.prototype.stopUpdateChecker = function() {
  384.   if (this.updateChecker_) {
  385.     this.updateChecker_.cancel();
  386.     this.updateChecker_ = null;
  387.   }
  388.   // Cancel the oneoff check from maybeToggleUpdateChecking.
  389.   if (this.currentUpdateChecker_) {
  390.     this.currentUpdateChecker_.cancel();
  391.     this.currentUpdateChecker_ = null;
  392.   }
  393. }
  394.  
  395. /**
  396.  * Provides an exception free way to look up the data in a table. We
  397.  * use this because at certain points our tables might not be loaded,
  398.  * and querying them could throw.
  399.  *
  400.  * @param table String Name of the table that we want to consult
  401.  * @param key String Key for table lookup
  402.  * @param callback nsIUrlListManagerCallback (ie., Function) given false or the
  403.  *        value in the table corresponding to key.  If the table name does not
  404.  *        exist, we return false, too.
  405.  */
  406. PROT_ListManager.prototype.safeExists = function(table, key, callback) {
  407.   try {
  408.     G_Debug(this, "safeExists: " + table + ", " + key);
  409.     var map = this.tablesData[table];
  410.     map.exists(key, callback);
  411.   } catch(e) {
  412.     G_Debug(this, "safeExists masked failure for " + table + ", key " + key + ": " + e);
  413.     callback.handleEvent(false);
  414.   }
  415. }
  416.  
  417. /**
  418.  * We store table versions in user prefs.  This method pulls the values out of
  419.  * the user prefs and into the tablesKnown objects.
  420.  */
  421. PROT_ListManager.prototype.loadTableVersions_ = function() {
  422.   // Pull values out of prefs.
  423.   var prefBase = kTableVersionPrefPrefix;
  424.   for (var table in this.tablesKnown_) {
  425.     var version = this.prefs_.getPref(prefBase + table, "1.-1");
  426.     G_Debug(this, "loadTableVersion " + table + ": " + version);
  427.     var tokens = version.split(".");
  428.     G_Assert(this, tokens.length == 2, "invalid version number");
  429.     
  430.     this.tablesKnown_[table].major = tokens[0];
  431.     this.tablesKnown_[table].minor = tokens[1];
  432.   }
  433. }
  434.  
  435. /**
  436.  * Callback from db update service.  As new tables are added to the db,
  437.  * this callback is fired so we can update the version number.
  438.  * @param versionString String containing the table update response from the
  439.  *        server
  440.  */
  441. PROT_ListManager.prototype.setTableVersion_ = function(versionString) {
  442.   G_Debug(this, "Got version string: " + versionString);
  443.   var versionParser = new PROT_VersionParser("");
  444.   if (versionParser.fromString(versionString)) {
  445.     var tableName = versionParser.type;
  446.     var versionNumber = versionParser.versionString();
  447.     var prefBase = kTableVersionPrefPrefix;
  448.  
  449.     this.prefs_.setPref(prefBase + tableName, versionNumber);
  450.     
  451.     if (!this.tablesKnown_[tableName]) {
  452.       this.tablesKnown_[tableName] = versionParser;
  453.     } else {
  454.       this.tablesKnown_[tableName].ImportVersion(versionParser);
  455.     }
  456.     
  457.     if (!this.tablesData[tableName])
  458.       this.tablesData[tableName] = newUrlClassifierTable(tableName);
  459.   }
  460.  
  461.   // Since this is called from the update server, it means there was
  462.   // a successful http request.  Make sure to notify the request backoff
  463.   // object.
  464.   this.requestBackoff_.noteServerResponse(200 /* ok */);
  465. }
  466.  
  467. /**
  468.  * Prepares a URL to fetch upates from. Format is a squence of 
  469.  * type:major:minor, fields
  470.  * 
  471.  * @param url The base URL to which query parameters are appended; assumes
  472.  *            already has a trailing ?
  473.  * @returns the URL that we should request the table update from.
  474.  */
  475. PROT_ListManager.prototype.getRequestURL_ = function(url) {
  476.   url += "version=";
  477.   var firstElement = true;
  478.   var requestMac = false;
  479.  
  480.   for (var type in this.tablesKnown_) {
  481.     // All tables with a major of 0 are internal tables that we never
  482.     // update remotely.
  483.     if (this.tablesKnown_[type].major == 0)
  484.       continue;
  485.  
  486.     // Check if the table needs updating
  487.     if (this.tablesKnown_[type].needsUpdate == false)
  488.       continue;
  489.  
  490.     if (!firstElement) {
  491.       url += ","
  492.     } else {
  493.       firstElement = false;
  494.     }
  495.     url += type + ":" + this.tablesKnown_[type].toUrl();
  496.  
  497.     if (this.tablesKnown_[type].requireMac)
  498.       requestMac = true;
  499.   }
  500.  
  501.   // Request a mac only if at least one of the tables to be updated requires
  502.   // it
  503.   if (requestMac) {
  504.     // Add the wrapped key for requesting macs
  505.     if (!this.urlCrypto_)
  506.       this.urlCrypto_ = new PROT_UrlCrypto();
  507.  
  508.     url += "&wrkey=" +
  509.       encodeURIComponent(this.urlCrypto_.getManager().getWrappedKey());
  510.   }
  511.  
  512.   G_Debug(this, "getRequestURL returning: " + url);
  513.   return url;
  514. }
  515.  
  516. /**
  517.  * Updates our internal tables from the update server
  518.  *
  519.  * @returns true when a new request was scheduled, false if an old request
  520.  *          was still pending.
  521.  */
  522. PROT_ListManager.prototype.checkForUpdates = function() {
  523.   // Allow new updates to be scheduled from maybeToggleUpdateChecking()
  524.   this.currentUpdateChecker_ = null;
  525.  
  526.   if (!this.updateserverURL_) {
  527.     G_Debug(this, 'checkForUpdates: no update server url');
  528.     return false;
  529.   }
  530.  
  531.   // See if we've triggered the request backoff logic.
  532.   if (!this.requestBackoff_.canMakeRequest())
  533.     return false;
  534.  
  535.   // Check to make sure our tables still exist (maybe the db got corrupted or
  536.   // the user deleted the file).  If not, we need to reset the table version
  537.   // before sending the update check.
  538.   var tableNames = [];
  539.   for (var tableName in this.tablesKnown_) {
  540.     tableNames.push(tableName);
  541.   }
  542.   var dbService = Cc["@mozilla.org/url-classifier/dbservice;1"]
  543.                   .getService(Ci.nsIUrlClassifierDBService);
  544.   dbService.checkTables(tableNames.join(","),
  545.                         BindToObject(this.makeUpdateRequest_, this));
  546.   return true;
  547. }
  548.  
  549. /**
  550.  * Method that fires the actual HTTP update request.
  551.  * First we reset any tables that have disappeared.
  552.  * @param tableNames String comma separated list of tables that
  553.  *   don't exist
  554.  */
  555. PROT_ListManager.prototype.makeUpdateRequest_ = function(tableNames) {
  556.   // Clear prefs that track table version if they no longer exist in the db.
  557.   var tables = tableNames.split(",");
  558.   for (var i = 0; i < tables.length; ++i) {
  559.     G_Debug(this, "Table |" + tables[i] + "| no longer exists, clearing pref.");
  560.     this.prefs_.clearPref(kTableVersionPrefPrefix + tables[i]);
  561.   }
  562.  
  563.   // Ok, now reload the table version.
  564.   this.loadTableVersions_();
  565.  
  566.   G_Debug(this, 'checkForUpdates: scheduling request..');
  567.   var url = this.getRequestURL_(this.updateserverURL_);
  568.   var streamer = Cc["@mozilla.org/url-classifier/streamupdater;1"]
  569.                  .getService(Ci.nsIUrlClassifierStreamUpdater);
  570.   try {
  571.     streamer.updateUrl = url;
  572.   } catch (e) {
  573.     G_Debug(this, 'invalid url');
  574.     return;
  575.   }
  576.  
  577.   if (!streamer.downloadUpdates(BindToObject(this.setTableVersion_, this),
  578.                                 BindToObject(this.downloadError_, this))) {
  579.     G_Debug(this, "pending update, wait until later");
  580.   }
  581. }
  582.  
  583. /**
  584.  * Callback function if there's a download error.
  585.  * @param status String http status or an empty string if connection refused.
  586.  */
  587. PROT_ListManager.prototype.downloadError_ = function(status) {
  588.   G_Debug(this, "download error: " + status);
  589.   // If status is empty, then we assume that we got an NS_CONNECTION_REFUSED
  590.   // error.  In this case, we treat this is a http 500 error.
  591.   if (!status) {
  592.     status = 500;
  593.   }
  594.   status = parseInt(status, 10);
  595.   this.requestBackoff_.noteServerResponse(status);
  596.  
  597.   // Try again in a minute
  598.   this.currentUpdateChecker_ =
  599.     new G_Alarm(BindToObject(this.checkForUpdates, this), 60000);
  600. }
  601.  
  602. PROT_ListManager.prototype.QueryInterface = function(iid) {
  603.   if (iid.equals(Ci.nsISupports) ||
  604.       iid.equals(Ci.nsIUrlListManager) ||
  605.       iid.equals(Ci.nsITimerCallback))
  606.     return this;
  607.  
  608.   Components.returnCode = Components.results.NS_ERROR_NO_INTERFACE;
  609.   return null;
  610. }
  611.  
  612. // A simple factory function that creates nsIUrlClassifierTable instances based
  613. // on a name.  The name is a string of the format
  614. // provider_name-semantic_type-table_type.  For example, goog-white-enchash
  615. // or goog-black-url.
  616. function newUrlClassifierTable(name) {
  617.   G_Debug("protfactory", "Creating a new nsIUrlClassifierTable: " + name);
  618.   var tokens = name.split('-');
  619.   var type = tokens[2];
  620.   var table = Cc['@mozilla.org/url-classifier/table;1?type=' + type]
  621.                 .createInstance(Ci.nsIUrlClassifierTable);
  622.   table.name = name;
  623.   return table;
  624. }
  625. /* ***** BEGIN LICENSE BLOCK *****
  626.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  627.  *
  628.  * The contents of this file are subject to the Mozilla Public License Version
  629.  * 1.1 (the "License"); you may not use this file except in compliance with
  630.  * the License. You may obtain a copy of the License at
  631.  * http://www.mozilla.org/MPL/
  632.  *
  633.  * Software distributed under the License is distributed on an "AS IS" basis,
  634.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  635.  * for the specific language governing rights and limitations under the
  636.  * License.
  637.  *
  638.  * The Original Code is Google Safe Browsing.
  639.  *
  640.  * The Initial Developer of the Original Code is Google Inc.
  641.  * Portions created by the Initial Developer are Copyright (C) 2006
  642.  * the Initial Developer. All Rights Reserved.
  643.  *
  644.  * Contributor(s):
  645.  *   Niels Provos <niels@google.com> (original author)
  646.  *   Fritz Schneider <fritz@google.com>
  647.  *
  648.  * Alternatively, the contents of this file may be used under the terms of
  649.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  650.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  651.  * in which case the provisions of the GPL or the LGPL are applicable instead
  652.  * of those above. If you wish to allow use of your version of this file only
  653.  * under the terms of either the GPL or the LGPL, and not to allow others to
  654.  * use your version of this file under the terms of the MPL, indicate your
  655.  * decision by deleting the provisions above and replace them with the notice
  656.  * and other provisions required by the GPL or the LGPL. If you do not delete
  657.  * the provisions above, a recipient may use your version of this file under
  658.  * the terms of any one of the MPL, the GPL or the LGPL.
  659.  *
  660.  * ***** END LICENSE BLOCK ***** */
  661.  
  662.  
  663. // A class that serializes and deserializes opaque key/value string to
  664. // string maps to/from maps (trtables). It knows how to create
  665. // trtables from the serialized format, so it also understands
  666. // meta-information like the name of the table and the table's
  667. // version. See docs for the protocol description.
  668. // 
  669. // TODO: wireformatreader: if you have multiple updates for one table
  670. //       in a call to deserialize, the later ones will be merged 
  671. //       (all but the last will be ignored). To fix, merge instead
  672. //       of replace when you have an existing table, and only do so once.
  673. // TODO must have blank line between successive types -- problem?
  674. // TODO doesn't tolerate blank lines very well
  675. //
  676. // Maybe: These classes could use a LOT more cleanup, but it's not a
  677. //       priority at the moment. For example, the tablesData/Known
  678. //       maps should be combined into a single object, the parser
  679. //       for a given type should be separate from the version info,
  680. //       and there should be synchronous interfaces for testing.
  681.  
  682.  
  683. /**
  684.  * A class that knows how to serialize and deserialize meta-information.
  685.  * This meta information is the table name and version number, and 
  686.  * in its serialized form looks like the first line below:
  687.  * 
  688.  * [name-of-table X.Y update?]                
  689.  * ...key/value pairs to add or delete follow...
  690.  * <blank line ends the table>
  691.  *
  692.  * The X.Y is the version number and the optional "update" token means 
  693.  * that the table is a differential from the curent table the extension
  694.  * has. Its absence means that this is a full, new table.
  695.  */
  696. function PROT_VersionParser(type, opt_major, opt_minor, opt_requireMac) {
  697.   this.debugZone = "versionparser";
  698.   this.type = type;
  699.   this.major = 0;
  700.   this.minor = 0;
  701.  
  702.   this.badHeader = false;
  703.  
  704.   // Should the wireformatreader compute a mac?
  705.   this.mac = false;
  706.   this.macval = "";
  707.   this.macFailed = false;
  708.   this.requireMac = !!opt_requireMac;
  709.  
  710.   this.update = false;
  711.   this.needsUpdate = false;  // used by ListManager to determine update policy
  712.   // Used by ListerManager to see if we have read data for this table from
  713.   // disk.  Once we read a table from disk, we are not going to do so again
  714.   // but instead update remotely if necessary.
  715.   this.didRead = false;
  716.   if (opt_major)
  717.     this.major = parseInt(opt_major);
  718.   if (opt_minor)
  719.     this.minor = parseInt(opt_minor);
  720. }
  721.  
  722. /** Import the version information from another VersionParser
  723.  * @params version a version parser object
  724.  */
  725. PROT_VersionParser.prototype.ImportVersion = function(version) {
  726.   this.major = version.major;
  727.   this.minor = version.minor;
  728.  
  729.   this.mac = version.mac;
  730.   this.macFailed = version.macFailed;
  731.   this.macval = version.macval;
  732.   // Don't set requireMac, since we create vparsers from scratch and doesn't
  733.   // know about it
  734. }
  735.  
  736. /** 
  737.  * Creates a string like [goog-white-black 1.1] from internal information
  738.  * 
  739.  * @returns String
  740.  */
  741. PROT_VersionParser.prototype.toString = function() {
  742.   var s = "[" + this.type + " " + this.major + "." + this.minor + "]";
  743.   return s;
  744. }
  745.  
  746. /**
  747.  * Creates a string like 1.123 with the version number.  This is the
  748.  * format we store in prefs.
  749.  * @return String
  750.  */
  751. PROT_VersionParser.prototype.versionString = function() {
  752.   return this.major + "." + this.minor;
  753. }
  754.  
  755. /** 
  756.  * Creates a string like 1:1 from internal information used for
  757.  * fetching updates from the server. Called by the listmanager.
  758.  * 
  759.  * @returns String
  760.  */
  761. PROT_VersionParser.prototype.toUrl = function() {
  762.   return this.major + ":" + this.minor;
  763. }
  764.  
  765. /**
  766.  * Process the old format, [type major.minor [update]]
  767.  *
  768.  * @returns true if the string could be parsed, false otherwise
  769.  */
  770. PROT_VersionParser.prototype.processOldFormat_ = function(line) {
  771.   if (line[0] != '[' || line.slice(-1) != ']')
  772.     return false;
  773.  
  774.   var description = line.slice(1, -1);
  775.  
  776.   // Get the type name and version number of this table
  777.   var tokens = description.split(" ");
  778.   this.type = tokens[0];
  779.   var majorminor = tokens[1].split(".");
  780.   this.major = parseInt(majorminor[0]);
  781.   this.minor = parseInt(majorminor[1]);
  782.   if (isNaN(this.major) || isNaN(this.minor))
  783.     return false;
  784.  
  785.   if (tokens.length >= 3) {
  786.      this.update = tokens[2] == "update";
  787.   }
  788.  
  789.   return true;
  790. }
  791.  
  792. /**
  793.  * Takes a string like [name-of-table 1.1 [update]][mac=MAC] and figures out the
  794.  * type and corresponding version numbers.
  795.  * @returns true if the string could be parsed, false otherwise
  796.  */
  797. PROT_VersionParser.prototype.fromString = function(line) {
  798.   G_Debug(this, "Calling fromString with line: " + line);
  799.   if (line[0] != '[' || line.slice(-1) != ']')
  800.     return false;
  801.  
  802.   // There could be two [][], so take care of it
  803.   var secondBracket = line.indexOf('[', 1);
  804.   var firstPart = null;
  805.   var secondPart = null;
  806.  
  807.   if (secondBracket != -1) {
  808.     firstPart = line.substring(0, secondBracket);
  809.     secondPart = line.substring(secondBracket);
  810.     G_Debug(this, "First part: " + firstPart + " Second part: " + secondPart);
  811.   } else {
  812.     firstPart = line;
  813.     G_Debug(this, "Old format: " + firstPart);
  814.   }
  815.  
  816.   if (!this.processOldFormat_(firstPart))
  817.     return false;
  818.  
  819.   if (secondPart && !this.processOptTokens_(secondPart))
  820.     return false;
  821.  
  822.   return true;
  823. }
  824.  
  825. /**
  826.  * Process optional tokens
  827.  *
  828.  * @param line A string [token1=val1 token2=val2...]
  829.  * @returns true if the string could be parsed, false otherwise
  830.  */
  831. PROT_VersionParser.prototype.processOptTokens_ = function(line) {
  832.   if (line[0] != '[' || line.slice(-1) != ']')
  833.     return false;
  834.   var description = line.slice(1, -1);
  835.   // Get the type name and version number of this table
  836.   var tokens = description.split(" ");
  837.  
  838.   for (var i = 0; i < tokens.length; i++) {
  839.     G_Debug(this, "Processing optional token: " + tokens[i]);
  840.     var tokenparts = tokens[i].split("=");
  841.     switch(tokenparts[0]){
  842.     case "mac":
  843.       this.mac = true;
  844.       if (tokenparts.length < 2) {
  845.         G_Debug(this, "Found mac flag but not mac value!");
  846.         return false;
  847.       }
  848.       // The mac value may have "=" in it, so we can't just use tokenparts[1].
  849.       // Instead, just take the rest of tokens[i] after the first "="
  850.       this.macval = tokens[i].substr(tokens[i].indexOf("=")+1);
  851.       break;
  852.     default:
  853.       G_Debug(this, "Found unrecognized token: " + tokenparts[0]);
  854.       break;
  855.     }
  856.   }
  857.  
  858.   return true;
  859. }
  860.  
  861. //@line 43 "/cygdrive/K/tinderbuild/src/flock/mozilla/toolkit/components/url-classifier/src/nsUrlClassifierListManager.js"
  862.  
  863. var modScope = this;
  864. function Init() {
  865.   // Pull the library in.
  866.   var jslib = Cc["@mozilla.org/url-classifier/jslib;1"]
  867.               .getService().wrappedJSObject;
  868.   Function.prototype.inherits = jslib.Function.prototype.inherits;
  869.   modScope.G_Preferences = jslib.G_Preferences;
  870.   modScope.G_PreferenceObserver = jslib.G_PreferenceObserver;
  871.   modScope.G_ObserverServiceObserver = jslib.G_ObserverServiceObserver;
  872.   modScope.G_Debug = jslib.G_Debug;
  873.   modScope.G_Assert = jslib.G_Assert;
  874.   modScope.G_debugService = jslib.G_debugService;
  875.   modScope.G_Alarm = jslib.G_Alarm;
  876.   modScope.BindToObject = jslib.BindToObject;
  877.   modScope.PROT_XMLFetcher = jslib.PROT_XMLFetcher;
  878.   modScope.PROT_UrlCrypto = jslib.PROT_UrlCrypto;
  879.   modScope.RequestBackoff = jslib.RequestBackoff;
  880.  
  881.   // We only need to call Init once.
  882.   modScope.Init = function() {};
  883. }
  884.  
  885. // Module object
  886. function UrlClassifierListManagerMod() {
  887.   this.firstTime = true;
  888.   this.cid = Components.ID("{ca168834-cc00-48f9-b83c-fd018e58cae3}");
  889.   this.progid = "@mozilla.org/url-classifier/listmanager;1";
  890. }
  891.  
  892. UrlClassifierListManagerMod.prototype.registerSelf = function(compMgr, fileSpec, loc, type) {
  893.   if (this.firstTime) {
  894.     this.firstTime = false;
  895.     throw Components.results.NS_ERROR_FACTORY_REGISTER_AGAIN;
  896.   }
  897.   compMgr = compMgr.QueryInterface(Ci.nsIComponentRegistrar);
  898.   compMgr.registerFactoryLocation(this.cid,
  899.                                   "UrlClassifier List Manager Module",
  900.                                   this.progid,
  901.                                   fileSpec,
  902.                                   loc,
  903.                                   type);
  904. };
  905.  
  906. UrlClassifierListManagerMod.prototype.getClassObject = function(compMgr, cid, iid) {  
  907.   if (!cid.equals(this.cid))
  908.     throw Components.results.NS_ERROR_NO_INTERFACE;
  909.   if (!iid.equals(Ci.nsIFactory))
  910.     throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  911.  
  912.   return this.factory;
  913. }
  914.  
  915. UrlClassifierListManagerMod.prototype.canUnload = function(compMgr) {
  916.   return true;
  917. }
  918.  
  919. UrlClassifierListManagerMod.prototype.factory = {
  920.   createInstance: function(outer, iid) {
  921.     if (outer != null)
  922.       throw Components.results.NS_ERROR_NO_AGGREGATION;
  923.     Init();
  924.     return (new PROT_ListManager()).QueryInterface(iid);
  925.   }
  926. };
  927.  
  928. var ListManagerModInst = new UrlClassifierListManagerMod();
  929.  
  930. function NSGetModule(compMgr, fileSpec) {
  931.   return ListManagerModInst;
  932. }
  933.